// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ControlsManager */
-const { Clutter, GObject, Meta, St } = imports.gi;
+const { Clutter, GObject, St } = imports.gi;
const Dash = imports.ui.dash;
const Main = imports.ui.main;
-const Params = imports.misc.params;
const ViewSelector = imports.ui.viewSelector;
-const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const Overview = imports.ui.overview;
var SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME;
-function getRtlSlideDirection(direction, actor) {
- let rtl = actor.text_direction == Clutter.TextDirection.RTL;
- if (rtl) {
- direction = direction == SlideDirection.LEFT
- ? SlideDirection.RIGHT : SlideDirection.LEFT;
- }
- return direction;
-}
-
-var SlideDirection = {
- LEFT: 0,
- RIGHT: 1,
-};
-
-var SlideLayout = GObject.registerClass({
- Properties: {
- 'slide-x': GObject.ParamSpec.double(
- 'slide-x', 'slide-x', 'slide-x',
- GObject.ParamFlags.READWRITE,
- 0, 1, 1),
- },
-}, class SlideLayout extends Clutter.FixedLayout {
- _init(params) {
- this._slideX = 1;
- this._direction = SlideDirection.LEFT;
-
- super._init(params);
- }
-
- vfunc_get_preferred_width(container, forHeight) {
- let child = container.get_first_child();
-
- let [minWidth, natWidth] = child.get_preferred_width(forHeight);
-
- minWidth *= this._slideX;
- natWidth *= this._slideX;
-
- return [minWidth, natWidth];
- }
-
- vfunc_allocate(container, box) {
- let child = container.get_first_child();
-
- let availWidth = Math.round(box.x2 - box.x1);
- let availHeight = Math.round(box.y2 - box.y1);
- let [, natWidth] = child.get_preferred_width(availHeight);
-
- // Align the actor inside the clipped box, as the actor's alignment
- // flags only determine what to do if the allocated box is bigger
- // than the actor's box.
- let realDirection = getRtlSlideDirection(this._direction, child);
- let alignX = realDirection == SlideDirection.LEFT
- ? availWidth - natWidth
- : availWidth - natWidth * this._slideX;
-
- let actorBox = new Clutter.ActorBox();
- actorBox.x1 = box.x1 + alignX;
- actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
- actorBox.y1 = box.y1;
- actorBox.y2 = actorBox.y1 + availHeight;
-
- child.allocate(actorBox);
- }
-
- // eslint-disable-next-line camelcase
- set slide_x(value) {
- if (this._slideX == value)
- return;
- this._slideX = value;
- this.notify('slide-x');
- this.layout_changed();
- }
-
- // eslint-disable-next-line camelcase
- get slide_x() {
- return this._slideX;
- }
-
- set slideDirection(direction) {
- this._direction = direction;
- this.layout_changed();
- }
-
- get slideDirection() {
- return this._direction;
- }
-});
-
var FaderControl = GObject.registerClass(
class FaderControl extends St.Widget {
_init(params) {
}
});
-var SlidingControl = GObject.registerClass(
-class SlidingControl extends FaderControl {
- _init(params) {
- params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
-
- this.layout = new SlideLayout();
- this.layout.slideDirection = params.slideDirection;
- super._init({
- layout_manager: this.layout,
- style_class: 'overview-controls',
- clip_to_allocation: true,
- });
-
- this._visible = true;
-
- Main.overview.connect('hiding', this._onOverviewHiding.bind(this));
- }
-
- _getSlide() {
- throw new GObject.NotImplementedError('_getSlide in %s'.format(this.constructor.name));
- }
-
- _updateSlide() {
- this.ease_property('@layout.slide-x', this._getSlide(), {
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- duration: SIDE_CONTROLS_ANIMATION_TIME,
- });
- }
-
- getVisibleWidth() {
- let child = this.get_first_child();
- let [, , natWidth] = child.get_preferred_size();
- return natWidth;
- }
-
- _getTranslation() {
- let child = this.get_first_child();
- let direction = getRtlSlideDirection(this.layout.slideDirection, child);
- let visibleWidth = this.getVisibleWidth();
-
- if (direction == SlideDirection.LEFT)
- return -visibleWidth;
- else
- return visibleWidth;
- }
-
- _updateTranslation() {
- let translationStart = 0;
- let translationEnd = 0;
- let translation = this._getTranslation();
-
- let shouldShow = this._getSlide() > 0;
- if (shouldShow)
- translationStart = translation;
- else
- translationEnd = translation;
-
- if (this.translation_x === translationEnd)
- return;
-
- this.translation_x = translationStart;
- this.ease({
- translation_x: translationEnd,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- duration: SIDE_CONTROLS_ANIMATION_TIME,
- });
- }
-
- _onOverviewHiding() {
- // We need to explicitly slideOut since showing pages
- // doesn't imply sliding out, instead, hiding the overview does.
- this.slideOut();
- }
-
- _onDragBegin() {
- super._onDragBegin();
- this._updateTranslation();
- this._updateSlide();
- }
-
- _onDragEnd() {
- super._onDragEnd();
- this._updateSlide();
- }
-
- slideIn() {
- this._visible = true;
- // we will update slide_x and the translation from pageEmpty
- }
-
- slideOut() {
- this._visible = false;
- this._updateTranslation();
- // we will update slide_x from pageEmpty
- }
-
- pageEmpty() {
- // When pageEmpty is received, there's no visible view in the
- // selector; this means we can now safely set the full slide for
- // the next page, since slideIn or slideOut might have been called,
- // changing the visibility
- this.remove_transition('@layout.slide-x');
- this.layout.slide_x = this._getSlide();
- this._updateTranslation();
- }
-});
-
-var ThumbnailsSlider = GObject.registerClass(
-class ThumbnailsSlider extends SlidingControl {
- _init(thumbnailsBox) {
- super._init({ slideDirection: SlideDirection.RIGHT });
-
- this._thumbnailsBox = thumbnailsBox;
-
- this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
- this.reactive = true;
- this.track_hover = true;
- this.add_actor(this._thumbnailsBox);
-
- Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this));
- global.workspace_manager.connect('active-workspace-changed',
- this._updateSlide.bind(this));
- global.workspace_manager.connect('notify::n-workspaces',
- this._updateSlide.bind(this));
- this.connect('notify::hover', this._updateSlide.bind(this));
- this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE);
- }
-
- _getAlwaysZoomOut() {
- // Always show the pager on hover, during a drag, or if workspaces are
- // actually used, e.g. there are windows on any non-active workspace
- let workspaceManager = global.workspace_manager;
- let alwaysZoomOut = this.hover ||
- this._inDrag ||
- !Meta.prefs_get_dynamic_workspaces() ||
- workspaceManager.n_workspaces > 2 ||
- workspaceManager.get_active_workspace_index() != 0;
-
- if (!alwaysZoomOut) {
- let monitors = Main.layoutManager.monitors;
- let primary = Main.layoutManager.primaryMonitor;
-
- /* Look for any monitor to the right of the primary, if there is
- * one, we always keep zoom out, otherwise its hard to reach
- * the thumbnail area without passing into the next monitor. */
- for (let i = 0; i < monitors.length; i++) {
- if (monitors[i].x >= primary.x + primary.width) {
- alwaysZoomOut = true;
- break;
- }
- }
- }
-
- return alwaysZoomOut;
- }
-
- getNonExpandedWidth() {
- let child = this.get_first_child();
- return child.get_theme_node().get_length('visible-width');
- }
-
- _onDragEnd() {
- this.sync_hover();
- super._onDragEnd();
- }
-
- _getSlide() {
- if (!this._visible)
- return 0;
-
- let alwaysZoomOut = this._getAlwaysZoomOut();
- if (alwaysZoomOut)
- return 1;
-
- let child = this.get_first_child();
- let preferredHeight = child.get_preferred_height(-1)[1];
- let expandedWidth = child.get_preferred_width(preferredHeight)[1];
-
- return this.getNonExpandedWidth() / expandedWidth;
- }
-
- getVisibleWidth() {
- let alwaysZoomOut = this._getAlwaysZoomOut();
- if (alwaysZoomOut)
- return super.getVisibleWidth();
- else
- return this.getNonExpandedWidth();
- }
-});
-
var DashFader = GObject.registerClass(
class DashFader extends FaderControl {
_init(dash) {
workspaceManager.connect('notify::n-workspaces',
this._updateAdjustment.bind(this));
- this._thumbnailsBox =
- new WorkspaceThumbnail.ThumbnailsBox(this._workspaceAdjustment);
- this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
-
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
this._workspaceAdjustment, this.dash.showAppsButton);
- this.viewSelector.connect('page-changed', this._setVisibility.bind(this));
- this.viewSelector.connect('page-empty', this._onPageEmpty.bind(this));
this._group = new St.BoxLayout({
name: 'overview-group',
y_expand: true,
});
box.add_child(this.viewSelector);
- box.add_child(this._thumbnailsSlider);
this._group.add_child(box);
this._group.add_actor(this._dashFader);
this._workspaceAdjustment.remove_transition('value');
this._workspaceAdjustment.value = activeIndex;
}
-
- _setVisibility() {
- // Ignore the case when we're leaving the overview, since
- // actors will be made visible again when entering the overview
- // next time, and animating them while doing so is just
- // unnecessary noise
- if (!Main.overview.visible ||
- (Main.overview.animationInProgress && !Main.overview.visibleTarget))
- return;
-
- let activePage = this.viewSelector.getActivePage();
- let thumbnailsVisible = activePage == ViewSelector.ViewPage.ACTIVITIES;
-
- if (thumbnailsVisible)
- this._thumbnailsSlider.slideIn();
- else
- this._thumbnailsSlider.slideOut();
- }
-
- _onPageEmpty() {
- this._thumbnailsSlider.pageEmpty();
- }
});
const OverviewControls = imports.ui.overviewControls;
const Search = imports.ui.search;
const ShellEntry = imports.ui.shellEntry;
+const Util = imports.misc.util;
+const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const WorkspacesView = imports.ui.workspacesView;
const EdgeDragAction = imports.ui.edgeDragAction;
var ActivitiesContainer = GObject.registerClass(
class ActivitiesContainer extends St.Widget {
- _init(workspacesDisplay, appDisplay, showAppsButton) {
+ _init(thumbnailsBox, workspacesDisplay, appDisplay, showAppsButton) {
super._init();
// 0 for window picker, 1 for app grid
showAppsButton.connect('notify::checked',
this._onShowAppsButtonToggled.bind(this));
+ this._thumbnailsBox = thumbnailsBox;
+ this.add_child(thumbnailsBox);
+
this._appDisplay = appDisplay;
this.add_child(appDisplay);
this._appDisplay.opacity = progress * 255;
this._appDisplay.visible = progress !== 0;
+
+ this._thumbnailsBox.set({
+ scale_x: Util.lerp(1, 0.5, progress),
+ scale_y: Util.lerp(1, 0.5, progress),
+ translation_y: Util.lerp(0, this._thumbnailsBox.height, progress),
+ opacity: Util.lerp(0, 255, 1 - progress),
+ visible: (1 - progress) !== 0,
+ });
}
- _getWorkspacesBoxes(box) {
+ _getWorkspacesBoxes(box, thumbnailsHeight) {
const initialBox = box.copy();
+ initialBox.y1 += thumbnailsHeight;
const finalBox = box.copy();
finalBox.set_size(
vfunc_allocate(box) {
this.set_allocation(box);
+ // Workspace Thumbnails
+ let thumbnailsHeight = 0;
+ if (this._thumbnailsBox.visible) {
+ const maxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE;
+ const primaryMonitor = Main.layoutManager.primaryMonitor;
+ const [width, height] = box.get_size();
+
+ [, thumbnailsHeight] =
+ this._thumbnailsBox.get_preferred_height(width);
+ thumbnailsHeight = Math.min(
+ thumbnailsHeight,
+ (primaryMonitor ? primaryMonitor.height : height) * maxThumbnailScale);
+
+ const thumbnailsBox = new Clutter.ActorBox();
+ thumbnailsBox.set_origin(0, 0);
+ thumbnailsBox.set_size(width, thumbnailsHeight);
+ this._thumbnailsBox.allocate(thumbnailsBox);
+ }
+
const progress = this._adjustment.value;
- const [initialBox, finalBox] = this._getWorkspacesBoxes(box);
+ const [initialBox, finalBox] =
+ this._getWorkspacesBoxes(box, thumbnailsHeight);
const workspacesBox = initialBox.interpolate(finalBox, progress);
this._workspacesDisplay.allocate(workspacesBox);
this._iconClickedId = 0;
this._capturedEventId = 0;
+ this._thumbnailsBox =
+ new WorkspaceThumbnail.ThumbnailsBox(workspaceAdjustment);
this._workspacesDisplay =
new WorkspacesView.WorkspacesDisplay(workspaceAdjustment);
this.appDisplay = new AppDisplay.AppDisplay();
const activitiesContainer = new ActivitiesContainer(
- this._workspacesDisplay, this.appDisplay, showAppsButton);
+ this._thumbnailsBox,
+ this._workspacesDisplay,
+ this.appDisplay,
+ showAppsButton);
this._activitiesPage =
this._addPage(activitiesContainer, _('Activities'), 'view-app-grid-symbolic');